package handler

import (
	"context"
	"crypto/sha512"
	"fmt"
	"go_final/mxshop_srvs/user_srv/global"
	"go_final/mxshop_srvs/user_srv/model"
	userpb "go_final/mxshop_srvs/user_srv/proto/protofile"
	"strings"
	"time"

	"github.com/anaskhan96/go-password-encoder"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"

	"gorm.io/gorm"

	"google.golang.org/protobuf/types/known/emptypb"
)

type Server struct{}

func Model2Response(user model.User) *userpb.UserInfo {
	userListResponse := userpb.UserInfo{
		ID:       user.ID,
		Mobile:   user.Mobile,
		Password: user.Password,
		NickName: user.NickName,
		// Birthday: user.Birthday,
		Gender: user.Gender,
		Role:   int32(user.Role),
	}

	// 在grpc的message中字段有默认值, 你不能随便赋值nil进去, 容易出错
	if user.Birthday != nil {
		userListResponse.Birthday = uint64(user.Birthday.Unix())
	}

	return &userListResponse
}

func Paginate(page, pageSize int) func(db *gorm.DB) *gorm.DB {
	return func(db *gorm.DB) *gorm.DB {
		if page == 0 {
			page = 1
		}

		switch {
		case pageSize > 100:
			pageSize = 100
		case pageSize <= 0:
			pageSize = 10
		}

		offset := (page - 1) * pageSize
		return db.Offset(offset).Limit(pageSize)
	}
}

func (s Server) GetUserList(ctx context.Context, info *userpb.PageInfo) (*userpb.UserListResponse, error) {
	fmt.Println("获取用户列表")
	var users []model.User
	result := global.DB.Find(&users)
	if result.Error != nil {
		return nil, result.Error
	}

	response := &userpb.UserListResponse{}
	response.Total = int32(result.RowsAffected)

	global.DB.Scopes(Paginate(int(info.PageNum), int(info.PageSize))).Find(&users)
	for _, user := range users {
		response.Data = append(response.Data, Model2Response(user))
	}

	return response, nil
}

func (s Server) GetUserByMobile(ctx context.Context, request *userpb.GetUserByMobileRequest) (*userpb.UserInfo, error) {
	var user model.User
	result := global.DB.Where(model.User{
		Mobile: request.Mobile,
	}).First(&user)
	if result.Error != nil {
		if result.Error == gorm.ErrRecordNotFound {
			return nil, status.Errorf(codes.NotFound, "用户不存在")
		}
		return nil, result.Error
	}

	return Model2Response(user), nil
}

func (s Server) GetUserByID(ctx context.Context, request *userpb.GetUserByIDRequest) (*userpb.UserInfo, error) {
	var user model.User
	result := global.DB.First(&user, request.ID)
	if result.Error != nil {
		if result.Error == gorm.ErrRecordNotFound {
			return nil, status.Errorf(codes.NotFound, "用户不存在")
		}
		return nil, result.Error
	}

	return Model2Response(user), nil
}

func (s Server) CreateUser(ctx context.Context, request *userpb.CreateUserRequest) (*userpb.UserInfo, error) {
	var user model.User
	res := global.DB.Where(model.User{
		Mobile: request.Mobile,
	}).First(&user)
	if res.RowsAffected > 0 {
		return nil, status.Errorf(codes.AlreadyExists, "用户已存在, 创建用户失败")
	}

	user.NickName = request.NickName
	user.Mobile = request.Mobile

	// 密码加密
	options := &password.Options{SaltLen: 16, Iterations: 100, KeyLen: 32, HashFunction: sha512.New}
	salt, encodedPwd := password.Encode("admin123", options)
	user.Password = fmt.Sprintf("$pbkdf2-sha512$%s$%s", salt, encodedPwd)

	res = global.DB.Create(&user)
	if res.Error != nil && res.Error != gorm.ErrRecordNotFound {
		return nil, status.Errorf(codes.Internal, res.Error.Error())
	}

	//return &userpb.UserInfo{
	//	ID: user.ID,
	//}, nil
	return Model2Response(user), nil
}

func (s Server) UpdateUser(ctx context.Context, request *userpb.UpdateUserRequest) (*emptypb.Empty, error) {
	var user model.User
	result := global.DB.First(&user, request.ID)
	if result.Error != nil {
		if result.Error == gorm.ErrRecordNotFound {
			return nil, status.Errorf(codes.NotFound, "用户不存在")
		}
		return nil, result.Error
	}

	// 更新方法1
	//res := global.DB.Model(&user).Updates(map[string]interface{}{
	//	"nick_name": request.NickName,
	//	"gender":    request.Gender,
	//	"birthday":  time.Unix(int64(request.Birthday), 0),
	//})

	// 更新方法2
	birthday := time.Unix(int64(request.Birthday), 0)
	user.NickName = request.NickName
	user.Gender = request.Gender
	user.Birthday = &birthday
	res := global.DB.Save(&user)

	if res.Error != nil && res.Error != gorm.ErrRecordNotFound {
		return nil, status.Errorf(codes.Internal, res.Error.Error())
	}

	return &emptypb.Empty{}, nil
}

func (s Server) CheckUserPassword(ctx context.Context, request *userpb.CheckUserPasswordRequest) (*userpb.
	CheckUserPasswordResponse, error) {
	// 密码校验
	passwdInfo := strings.Split(request.EncryptedPassword, "$")
	options := &password.Options{SaltLen: 16, Iterations: 100, KeyLen: 32, HashFunction: sha512.New}
	check := password.Verify(request.Password, passwdInfo[2], passwdInfo[3], options)
	return &userpb.CheckUserPasswordResponse{
		Checked: check,
	}, nil
}
